﻿using System;
using System.Linq.Expressions;
using System.Reflection;
using LazyMapper.Core.Helpers;

namespace LazyMapper.Core
{
    public class ValueSetter<TOutput, TType> : IPropertySetter<TOutput>
    {
        private readonly MemberExpression _expression;
        private readonly Expression<Func<TOutput, TType>> _memberExpression;
        private readonly PropertyInfo _propertyInfo;
        private readonly string _memberName;
        private Action<TOutput, TType> _compile;

        public ValueSetter(Expression<Func<TOutput, TType>> memberExpression)
        {
            _memberExpression = memberExpression;
            _expression = _memberExpression.Body as MemberExpression;
            if (_expression == null)
                throw new ArgumentException("Expression must be a MemberExpression.", "memberExpression");
            //initialize _propertyInfo
            _propertyInfo = typeof (TOutput).GetProperty(_expression.Member.Name);

            if (_propertyInfo == null)
                throw new ArgumentException(
                    string.Format("Could not find property info of member {0}.", _expression.Member.Name),
                    "memberExpression");
            if (_propertyInfo.GetSetMethod() == null)
            {
                throw new ArgumentException(string.Format("Setter for {0} must be public", _expression.Member.Name),
                                            "memberExpression");
            }
            _compile = memberExpression.ToFieldAssignment();
            _memberName = _expression.Member.Name;
        }

        public ValueSetter(string memberName, Action<TOutput, TType> action)
        {
            _memberName = memberName;
            _compile = action;
        }

        public Expression<Func<TOutput, TType>> MemberExpression { get; set; }

        #region IPropertySetter<TOutput> Members

        public void SetValue(TOutput value, object type)
        {
            if (_compile != null)
            {
                _compile(value, (TType)type);
            }
            else
            {
                _propertyInfo.SetValue(value, type, null);
            }
        }

        public string MemberName
        {
            get { return _memberName ; }
        }

        #endregion
    }

    
}